﻿using System;
using JetBrains.Annotations;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.Xaml.Impl;
using JetBrains.ReSharper.Psi.Xaml.Impl.Resolve;
using JetBrains.ReSharper.Psi.Xaml.Impl.Tree;
using JetBrains.ReSharper.Psi.Xaml.Tree;

namespace GammaJul.ReSharper.GammaXaml.Psi.Xaml {

	/// <summary>
	/// Contains extension methods related to <see cref="RelativeSourceMode"/>.
	/// </summary>
	public static class RelativeSourceExtensions {

		/// <summary>
		/// Returns a corresponding keyword for a given <see cref="RelativeSourceMode"/>.
		/// </summary>
		/// <param name="relativeSourceMode">The relative source mode.</param>
		/// <returns>A keyword corresponding to <paramref name="relativeSourceMode"/>.</returns>
		[NotNull]
		public static string ToKeyword(this RelativeSourceMode relativeSourceMode) {
			switch (relativeSourceMode) {
				case RelativeSourceMode.FIND_ANCESTOR:
					return XamlKeywords.RELATIVE_SOURCE_MODE_FIND_ANCESTOR;
				case RelativeSourceMode.PREVIOUS_DATA:
					return XamlKeywords.RELATIVE_SOURCE_MODE_PREVIOUS_DATA;
				case RelativeSourceMode.SELF:
					return XamlKeywords.RELATIVE_SOURCE_MODE_SELF;
				case RelativeSourceMode.TEMPLATED_PARENT:
					return XamlKeywords.RELATIVE_SOURCE_MODE_TEMPLATED_PARENT;
				default:
					return String.Empty;
			}
		}

		/// <summary>
		/// Returns a corresponding <see cref="RelativeSourceMode"/> for a given keyword.
		/// </summary>
		/// <param name="keyword">The relative source mode as a string.</param>
		/// <returns>A <see cref="RelativeSourceMode"/> corresponding to <paramref name="keyword"/>.</returns>
		public static RelativeSourceMode ToRelativeSourceMode([CanBeNull] this string keyword) {
			if (keyword == XamlKeywords.RELATIVE_SOURCE_MODE_FIND_ANCESTOR)
				return RelativeSourceMode.FIND_ANCESTOR;
			if (keyword == XamlKeywords.PREVIOUS_DATA)
				return RelativeSourceMode.PREVIOUS_DATA;
			if (keyword == XamlKeywords.SELF)
				return RelativeSourceMode.SELF;
			if (keyword == XamlKeywords.TEMPLATED_PARENT)
				return RelativeSourceMode.TEMPLATED_PARENT;
			return RelativeSourceMode.UNKNOWN;
		}

		/// <summary>
		/// Returns the <see cref="RelativeSourceMode"/> of a <see cref="IXStatic"/> object.
		/// </summary>
		/// <param name="xStatic">A <see cref="IXStatic"/> object, representing a relative source.</param>
		/// <returns>A <see cref="RelativeSourceMode"/> for <paramref name="xStatic"/>.</returns>
		public static RelativeSourceMode GetRelativeSourceMode([CanBeNull] this IXStatic xStatic) {
			if (xStatic == null)
				return RelativeSourceMode.UNKNOWN;

			ITypeMemberReference reference = xStatic.MemberReference;
			if (reference == null)
				return RelativeSourceMode.UNKNOWN;

			var property = reference.Resolve().DeclaredElement as IProperty;
			if (property == null)
				return RelativeSourceMode.UNKNOWN;

			IDeclaredType relativeSourceType = XamlTypeCache.GetCache(xStatic).RelativeSource;
			if (!Equals(property.Type, relativeSourceType))
				return RelativeSourceMode.UNKNOWN;

			ITypeElement containingType = property.GetContainingType();
			if (containingType == null || !Equals(containingType, relativeSourceType.GetTypeElement()))
				return RelativeSourceMode.UNKNOWN;

			return property.ShortName.ToRelativeSourceMode();
		}
		 
	}
}